// Copyright 2007-2010 Comdiv (F. Sadykov) - http://code.google.com/u/fagim.sadykov/
// Supported by Media Technology LTD 
//  
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//  
//      http://www.apache.org/licenses/LICENSE-2.0
//  
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 
// MODIFICATIONS HAVE BEEN MADE TO THIS FILE
using System;
using System.Configuration;
using System.Linq;
using Comdiv.Application;
using Comdiv.Extensions;
using Comdiv.Inversion;
using Comdiv.Model.Interfaces;
using FluentNHibernate.Mapping;

namespace Comdiv.Model.Mapping{
    public static class ClassMapExtension{
        private static IInversionContainer _container;
        public static object sync = new object();
        public static IInversionContainer Container
        {
            get
            {
                if (_container.invalid())
                {
                    lock (sync)
                    {
                        if (_container.invalid())
                        {
                            Container = myapp.ioc;
                        }
                    }
                }
                return _container;
            }
            set { _container = value; }
        }

        public static void standard<T>(this ClassMap<T> map,params Type[] excludeModelTypes) {
            var tablename = typeof (T).Name.ToLower().replace("poco$","");
            var tn = map.GetType().getFirstAttribute<TableNameAttribute>();
            if(null!=tn){
                tablename = tn.Name;
            }
            var rewrited = Container.all<IMapTableRewriter<T>>().Select(x=>x.GetTableName()).FirstOrDefault(x=>x.hasContent());
            if (rewrited.hasContent())
            {
                tablename = rewrited;
            }
            map.WithTable(tablename);

            if (Is<T,IWithId>(excludeModelTypes)){
                var id = map.Id(i => ((IWithId) i).Id);
                if (map is IDbSpecified && ((IDbSpecified)map).DbType=="postgres"){
                    id.GeneratedBy.Sequence(tablename + "_id_seq");
                }
            }
            if (Is<T,IWithCode>(excludeModelTypes))
            {
                map.Map(i => ((IWithCode) i).Code)
                    .Not.Nullable().Unique();
            }

            if (Is<T,IWithName>(excludeModelTypes))
            {
                var map_ = map.Map(i => ((IWithName) i).Name).WithLengthOf(20000);//.Not.Nullable();
                if(!Is<T,IWithCode>()){
                    map_.Unique().Not.Nullable();
                }    
            }

            if (Is<T, IWithNewTags>(excludeModelTypes)) {
                map.Map(i => ((IWithNewTags)i).Tag).Nullable();
            }

            if (Is<T,IWithComment>(excludeModelTypes))
            {
                map.Map(i => ((IWithComment)i).Comment).Nullable();
            }

            if (Is<T,IWithVersion>(excludeModelTypes))
            {
                map.Version(i => ((IWithVersion) i).Version);
            }

            //if (Is<T,IWithUid>(excludeModelTypes))
            //{
            //    map.Map(i => ((IWithUid) i).Uid).Unique();
            //}

            if (Is<T, IWithIdx>(excludeModelTypes))
            {
                map.Map(i => ((IWithIdx)i).Idx).Nullable();
            }
            if (Is<T,IUsrAware>(excludeModelTypes)){
                map.Map(i => ((IUsrAware)i).Usr).Nullable();
                map.Map(i => ((IUsrAware)i).LastUsr).Nullable();
                map.Map(i => ((IUsrAware)i).Created).Nullable();
                map.Map(i => ((IUsrAware)i).Edited).Nullable();
            }

            if (Is<T, IWithRange>(excludeModelTypes)){
                var _DateRange = map.Component<DateRange>(x =>((IWithRange) x).Range, c => { });
                _DateRange.Map(c => c.Start);
                _DateRange.Map(c => c.Finish);
                _DateRange.SetAttribute("class", typeof (DateRange).AssemblyQualifiedName);
            }


        }

        public static OneToManyPart<C> Standard<C>(this OneToManyPart<C> part,string columnName){
            columnName =
                Container.all<IColumnRenamer>().Select(x => x.GetReplace(columnName)).FirstOrDefault(x => x.hasContent()) ??
                columnName;
            part.KeyColumnNames.Add(columnName);
            part.LazyLoad().FetchType.Select();
            part.Cascade.None();
            return part;
        }

        public static ManyToOnePart<I> Standard<I, T>(this ManyToOnePart<I> part,string columnName)where T:I
        {
            columnName =
                Container.all<IColumnRenamer>().Select(x => x.GetReplace(columnName)).FirstOrDefault(x => x.hasContent()) ??
                columnName;
            return part.Standard<I, T>(columnName, true);
        }

        public static ManyToOnePart<I> Standard<I,T>(this ManyToOnePart<I> part){
            return part.Standard<I, T>(null, true);
        }

        public static ManyToOnePart<I> Standard<I, T>(this ManyToOnePart<I> part,string columnName,bool notNullable)
        {
            columnName =
                Container.all<IColumnRenamer>().Select(x => x.GetReplace(columnName)).FirstOrDefault(x => x.hasContent()) ??
                columnName;
            part.WithForeignKey()
                .FetchType.Select()
                .LazyLoad()
                .SetAttribute("class", typeof(T).AssemblyQualifiedName);
            if(notNullable){
                part.Not.Nullable();
            }
            if(columnName.hasContent()){
                part.ColumnName(columnName);
            }
            return part;
        }

       

        public static bool Is<T,I>(params Type[] excludeModelTypes)
        {
            if(excludeModelTypes.Contains(typeof(I))) return false;
            return typeof (I).IsAssignableFrom(typeof (T));
        }
    }

    public class TableNameAttribute:Attribute{
        public TableNameAttribute(string name){
            Name = name;
        }
        public string Name { get; protected set; }
    }
}